
package com.bluelinelabs.conductor.conductor.internal;


import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.AbilityLifecycleCallbacks;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.fraction.Fraction;
import ohos.aafwk.ability.fraction.FractionAbility;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.IntentParams;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.LayoutScatter;
import ohos.app.AbilityContext;
import ohos.app.ElementsCallback;
import ohos.global.configuration.Configuration;
import ohos.utils.PacMap;

import com.bluelinelabs.conductor.conductor.ActivityHostedRouter;
import com.bluelinelabs.conductor.conductor.Router;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * lipeiquan
 *
 * @since 2021-05-06
 */
public class LifecycleHandler extends Fraction implements AbilityLifecycleCallbacks, ElementsCallback {

    private static final String KEY_ROUTER_STATE_PREFIX = "LifecycleHandler.routerState";

    private AbilityContext ability;
    private boolean hasRegisteredCallbacks;
    private boolean destroyed;
    private boolean attached;
    private boolean hasPreparedForHostDetach;


    private static final Map<AbilityContext, LifecycleHandler> activeLifecycleHandlers = new HashMap<>();

    private HashMap<Integer, Object> activityRequestMap = new HashMap<>();


    private final Map<Integer, ActivityHostedRouter> routerMap = new HashMap<>();


    private LifecycleHandler() {

    }


    public static LifecycleHandler install(AbilityContext ability, ComponentContainer container) {
        LifecycleHandler lifecycleHandler = findInActivity(ability);
        if (lifecycleHandler == null) {
            lifecycleHandler = new LifecycleHandler();
            ((FractionAbility) ((AbilitySlice) ability).getAbility()).getFractionManager()
                .startFractionScheduler()
                .add(container.getId(), lifecycleHandler)
                .submit();
        }
        lifecycleHandler.registerActivityListener(ability);
        return lifecycleHandler;
    }

    public Router getRouter(ComponentContainer container, IntentParams savedInstanceState) {
        ActivityHostedRouter router = routerMap.get(getRouterHashKey(container));
        if (router == null) {
            router = new ActivityHostedRouter();
            router.setHost(this, container);

            if (savedInstanceState != null) {
                IntentParams routerSavedState = (IntentParams) savedInstanceState.getParam(KEY_ROUTER_STATE_PREFIX + router.getContainerId());
                if (routerSavedState != null) {
                    router.restoreInstanceState(routerSavedState);
                }
            }
            routerMap.put(getRouterHashKey(container), router);
        } else {
            router.setHost(this, container);
        }

        return router;
    }

    private static int getRouterHashKey(ComponentContainer viewGroup) {
        return viewGroup.getId();
    }


    public List<Router> getRouters() {
        return new ArrayList<Router>(routerMap.values());
    }

    public AbilityContext getLifecycleActivity() {
        return ability;
    }


    private static LifecycleHandler findInActivity(AbilityContext ability) {
        LifecycleHandler lifecycleHandler = activeLifecycleHandlers.get(ability);
        if (lifecycleHandler == null) {
//            lifecycleHandler = (LifecycleHandler)(((FractionAbility) ((AbilitySlice) ability).getAbility()).getFractionManager().getFractionByTag(FRAGMENT_TAG)).get();
        }
        if (lifecycleHandler != null) {
            lifecycleHandler.registerActivityListener(ability);
        }
        return lifecycleHandler;
    }

    private void registerActivityListener(AbilityContext ability) {
        this.ability = ability;

        if (!hasRegisteredCallbacks) {
            hasRegisteredCallbacks = true;
            ((AbilitySlice) ability).getAbility().getAbilityPackage().registerCallbacks(this, this);

            // Since Fragment transactions are async, we have to keep an <Activity, LifecycleHandler> map in addition
            // to trying to find the LifecycleHandler fragment in the Activity to handle the case of the developer
            // trying to immediately get > 1 router in the same Activity. See issue #299.
            activeLifecycleHandlers.put(ability, this);
        }
    }

    public void registerForActivityResult(String instanceId, int requestCode) {
        activityRequestMap.put(requestCode, instanceId);
    }

    public void unregisterForActivityResults(String instanceId) {
        for (int i = activityRequestMap.size() - 1; i >= 0; i--) {
            if (instanceId.equals(activityRequestMap.get(activityRequestMap.get(i)))) {
                activityRequestMap.remove(i);
            }
        }
    }


    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        String instanceId = (String) activityRequestMap.get(requestCode);
        if (instanceId != null) {
            for (Router router : getRouters()) {
                router.onActivityResult(instanceId, requestCode, resultCode, data);
            }
        }
    }


    @Override
    public void onAbilityStart(Ability ability) {

    }

    @Override
    public void onAbilityActive(Ability ability) {

    }

    @Override
    public void onAbilityInactive(Ability ability) {
        System.out.println("wtf LifecycleHandler onAbilityInactive ");
    }

    @Override
    public void onAbilityForeground(Ability ability) {

    }

    @Override
    public void onAbilityBackground(Ability ability) {

    }

    @Override
    public void onAbilityStop(Ability ability) {

    }

    @Override
    public void onAbilitySaveState(PacMap pacMap) {
    }

    @Override
    public void onMemoryLevel(int i) {
    }

    @Override
    public void onConfigurationUpdated(Configuration configuration) {
    }

    @Override
    protected Component onComponentAttached(LayoutScatter scatter, ComponentContainer container, Intent intent) {
        destroyed = false;
        setAttached();
        return super.onComponentAttached(scatter, container, intent);
    }

    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);
        if (this.ability == ability) {
            hasPreparedForHostDetach = false;
            for (Router router : getRouters()) {
                router.onActivityStarted(ability);
            }
        }
    }

    @Override
    protected void onActive() {
        super.onActive();
        if (this.ability == ability) {
            for (Router router : getRouters()) {
                router.onActivityResumed(ability);
            }
        }
    }

    @Override
    protected void onInactive() {
        super.onInactive();
    }

    @Override
    protected void onForeground(Intent intent) {
        super.onForeground(intent);
    }

    @Override
    protected void onBackground() {
        super.onBackground();
        if (this.ability == ability) {
            for (Router router : getRouters()) {
                router.onActivityPaused(ability);
            }
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (this.ability == ability) {
            prepareForHostDetachIfNeeded();

            for (Router router : getRouters()) {
                router.onActivityStopped(ability);
            }
        }
    }

    private void prepareForHostDetachIfNeeded() {
        if (!hasPreparedForHostDetach) {
            hasPreparedForHostDetach = true;

            for (Router router : getRouters()) {
                router.prepareForHostDetach();
            }
        }
    }

    private void setAttached() {
        if (!attached) {
            attached = true;

//            for (int i = pendingPermissionRequests.size() - 1; i >= 0; i--) {
//                PendingPermissionRequest request = pendingPermissionRequests.remove(i);
//                requestPermissions(request.instanceId, request.permissions, request.requestCode);
//            }

            for (ActivityHostedRouter router : new ArrayList<>(routerMap.values())) {
                router.onContextAvailable();
            }
        }
    }

    private void destroyRouters(boolean configurationChange) {
        if (!destroyed) {
            destroyed = true;

            if (ability != null) {
                for (Router router : getRouters()) {
                    router.onActivityDestroyed(ability, configurationChange);
                }
            }
        }
    }
}
